#include "wl_audio.h"

#include "wl_core.h"
#include "wl_malloc.h"
#include "wl_mutex.h"
#include "wl_trace.h"
#include "wl_utils.h"

#define INVALID_AUDIO_HANDLE 0

#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
#    include <Aud_gen.h>
#    include <adm.h>
#    include <audio_api.h>
#    include <dsp_codec_adp.h>
#    include <wav_adp.h>
extern HAUDIOCODEC hARMVBDualChn;
extern HAUDIOCODEC hARMVBDualChnRecord;
extern HAUDIOCODEC hARMVB;
extern HAUDIOCODEC hARMVBRECORD;

// #    define CHANGE_AUDIO_MODE
#    undef INVALID_AUDIO_HANDLE
#    define INVALID_AUDIO_HANDLE INVALID_HANDLE
#elif defined(WL_OS_QUECTEL)
#    include "wlsdk_define.h"
#    include "ql_audio.h"
#    include "ql_gpio.h"
#    include "ql_rtos.h"
#    include "wl_timer.h"

#    undef INVALID_AUDIO_HANDLE
#    define INVALID_AUDIO_HANDLE NULL
#else
#    include <SDL2/SDL.h>
#    include <SDL2/SDL_audio.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
#    define PLAYER_CODEC_CACHE_SIZE (PLAY_PCM_FRAME_BYTES * 5)
#elif defined(WL_OS_QUECTEL)
#    define QL_PCM_BLOCK_FLAG (0x01)
#    define QL_PCM_NONBLOCK_FLAG (0x02)
#    define QL_PCM_READ_FLAG (0x04)
#    define QL_PCM_WRITE_FLAG (0x08)
#else
#endif

typedef struct __wl_audio_recorder {
    WL_MUTEX                   mutex;
    wl_audio_recorder_callback callback;
    wl_audio_end_callback      end_callback;
    wl_uint8                   cache[RECORD_PCM_CACHED_LEN];
    wl_int32                   cachelen;
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    HAUDIO handle;
#elif defined(WL_OS_QUECTEL)
#    if !defined(NEW_QUECTEL_AUDIO_API)
    PCM_HANDLE_T handle;
#    else
    audio_record_handle handle;
#    endif
    WL_TIMER timer;
#else
    SDL_AudioDeviceID handle;
#endif
} wl_audio_recorder;

typedef struct __wl_audio_player {
    WL_MUTEX                 mutex;
    wl_audio_player_callback callback;
    wl_audio_end_callback    end_callback;
    wl_int32                 cachelen;
    wl_uint8                 cache[PLAY_PCM_CACHED_LEN];
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    HAUDIO                   handle;
    AUDIO_DEVICE_MODE_TYPE_E devicemode;
    uint32                   volume;
    wl_uint8                 play_cache[PLAY_PCM_CACHED_LEN];
    wl_uint32                codec_cache[PLAYER_CODEC_CACHE_SIZE];
#elif defined(WL_OS_QUECTEL)
#    if !defined(NEW_QUECTEL_AUDIO_API)
    PCM_HANDLE_T handle;
#    else
    audio_track_handle handle;
#    endif
    WL_TIMER timer;
    int      volume;
#else
    SDL_AudioDeviceID handle;
#endif
} wl_audio_player;

typedef struct __wl_audio_global {
    wl_bool           initialized;
    wl_audio_recorder recorder;
    wl_audio_player   player;
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
    BLOCK_ID thread;
    wl_bool  exit;
#elif defined(WL_OS_QUECTEL)
#endif
} wl_audio_global;
static wl_audio_global s_audio_global;

#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
typedef enum {
    AUDIO_SIGNAL_RECORD_END = 0,
    AUDIO_SIGNAL_RECORD_DATA,
    AUDIO_SIGNAL_PLAY_END,
    AUDIO_SIGNAL_GET_PLAY_DATA
} wl_audio_signalid;

typedef struct __wl_audio_signal {
    wl_int32 signalid;
    wl_char  content[ZERO_CONTENT_SIZE];
} wl_audio_signal;

typedef struct __wl_audio_signal_end {
    wl_int32 resultcode;
} wl_audio_signal_end;

typedef struct __wl_audio_signal_record_frame {
    wl_uint8  data[RECORD_PCM_FRAME_BYTES];
    wl_uint16 datasize;
} wl_audio_signal_record_frame;

static void audio_main_thread(uint32 argc, void* param);
static void audio_notify_callback(HAUDIO hAudio, uint32 notify_info, uint32 affix_info);
static AUDIO_STREAM_GET_SRC_RESULT_E
    audio_pcm_callback(HAUDIO hAudio, AUDIO_GETSRCDATA_INFO_T* const ptSrcDataInfo, void* pvOtherInfo);
#elif defined(WL_OS_QUECTEL)
extern wlsdk_enablePA wlsdk_get_PA_control_callback();
#else
#endif

wl_int32 audio_put_play_data(WL_AUDIO_HANDLE handle, wl_uint8* data, wl_uint32 data_size);
wl_int32 audio_get_play_data(wl_uint8* data, wl_uint32 datalen);

wl_int32 wl_audio_init(void) {
    WLLOGI("audio init");
    if (!s_audio_global.initialized) {
        if (0 != wl_mutex_create("player", &s_audio_global.player.mutex)) {
            WLLOGW("wl_mutex_create player fail");
            return -1;
        }
        if (0 != wl_mutex_create("recorder", &s_audio_global.recorder.mutex)) {
            WLLOGW("wl_mutex_create recorder fail");
            wl_mutex_delete(&s_audio_global.player.mutex);
            return -1;
        }
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
        s_audio_global.exit = wl_false;

        s_audio_global.player.handle   = INVALID_AUDIO_HANDLE;
        s_audio_global.recorder.handle = INVALID_AUDIO_HANDLE;

        s_audio_global.thread = SCI_CreateThread(
            "wl_audio", "wl_audio_queue", audio_main_thread, 0, wl_null, 8 * 1024, 30, 20, SCI_PREEMPT, SCI_AUTO_START);
        if (SCI_INVALID_BLOCK_ID == s_audio_global.thread) {
            wl_mutex_delete(&s_audio_global.player.mutex);
            wl_mutex_delete(&s_audio_global.recorder.mutex);
            WLLOGW("SCI_CreateThread fail");
            return -1;
        }
#elif defined(WL_OS_QUECTEL)
        s_audio_global.player.handle   = INVALID_AUDIO_HANDLE;
        s_audio_global.recorder.handle = INVALID_AUDIO_HANDLE;
#else
        if (0 != SDL_Init(SDL_INIT_AUDIO)) {
            wl_mutex_delete(&s_audio_global.player.mutex);
            wl_mutex_delete(&s_audio_global.recorder.mutex);
            WLLOGW("SDL_Init fail");
            return -1;
        }
#endif
        s_audio_global.initialized = wl_true;
    }
    WLLOGI("audio init success");
    return 0;
}

wl_int32 wl_audio_end(void) {
    if (s_audio_global.initialized) {
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
        s_audio_global.exit = wl_true;
        if (INVALID_AUDIO_HANDLE != s_audio_global.recorder.handle) {
            wl_uint32 tmp = 0;
            AUDIO_ExeCodecExtOpe(s_audio_global.recorder.handle,
                                 ANSI2UINT16("EXT_STOP_RECORD"),
                                 1,
                                 (void*)&s_audio_global.recorder.handle,
                                 &tmp);
            AUDIO_CloseHandle(s_audio_global.recorder.handle);
            s_audio_global.recorder.handle = INVALID_AUDIO_HANDLE;
        }
        if (INVALID_AUDIO_HANDLE != s_audio_global.player.handle) {
            AUDIO_Stop(s_audio_global.player.handle);
            AUDIO_CloseHandle(s_audio_global.player.handle);
            s_audio_global.player.handle = INVALID_AUDIO_HANDLE;
        }
#elif defined(WL_OS_QUECTEL)
#else
        SDL_Quit();
#endif
        wl_mutex_release(&s_audio_global.player.mutex);
        wl_mutex_release(&s_audio_global.recorder.mutex);
#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
        SCI_DeleteThread(s_audio_global.thread);
#elif defined(WL_OS_QUECTEL)
#else
#endif
        wl_mutex_delete(&s_audio_global.player.mutex);
        wl_mutex_delete(&s_audio_global.recorder.mutex);
        s_audio_global.initialized = wl_false;
    }
    return 0;
}

#if defined(WL_OS_MG)
#elif defined(WL_OS_U8910)
static void audio_notify_callback(HAUDIO hAudio, uint32 notify_info, uint32 affix_info) {
    WLLOGI("audio_notify_callback notify_info %d affix_info %d", notify_info, affix_info);
}

void audio_main_thread(uint32 argc, void* param) {
    BLOCK_ID thread = SCI_IdentifyThread();

    WLLOGI("audio_main_thread start");
    while (!s_audio_global.exit) {
        wl_audio_signal* signal = (wl_audio_signal*)SCI_GetSignal(thread);
        WLLOGT("audio %d signal", signal->signalid);
        if (signal) {
            wl_mutex_acquire(&s_audio_global.recorder.mutex);
            switch (signal->signalid) {
                case AUDIO_SIGNAL_RECORD_END: {
                    wl_audio_end_callback end_callback = s_audio_global.recorder.end_callback;
                    wl_audio_signal_end*  signal_end   = (wl_audio_signal_end*)signal->content;
                    if (end_callback) {
                        end_callback(s_audio_global.recorder.handle, signal_end->resultcode);
                    }
                    break;
                }
                case AUDIO_SIGNAL_RECORD_DATA: {
                    wl_audio_recorder_callback    callback     = s_audio_global.recorder.callback;
                    wl_audio_signal_record_frame* record_frame = (wl_audio_signal_record_frame*)signal->content;
                    if (callback) {
                        callback(s_audio_global.recorder.handle, record_frame->data, record_frame->datasize);
                    }
                }
                default:
                    break;
            }
            wl_mutex_release(&s_audio_global.recorder.mutex);
            wl_free(signal);
        }
    }
    SCI_ThreadExit();
}

static void audio_recorder_callback(wl_uint16 framelen) {
    DSP_CODEC_EXT_OPE_VOICE_DATA_T param;
    wl_uint32                      tmp    = 0;
    HAUDIO                         handle = s_audio_global.recorder.handle;
    if (framelen > RECORD_PCM_CACHED_LEN) {
        WLLOGW("framelen %d too long", framelen);
        return;
    }
    if ((framelen << 1) + s_audio_global.recorder.cachelen > RECORD_PCM_CACHED_LEN) {
        s_audio_global.recorder.cachelen = 0;
        WLLOGW("cached error");
    }
    param.source_buf_ptr = (wl_uint16*)(s_audio_global.recorder.cache + s_audio_global.recorder.cachelen);
    param.data_length    = framelen;

    if (INVALID_AUDIO_HANDLE != handle) {
        if (AUDIO_NO_ERROR
            != AUDIO_ExeCodecExtOpe(
                handle, ANSI2UINT16("READ_VOICE_DATA"), DSP_CODEC_EXT_OPE_VOICE_DATA_PARA_COUNT, &param, &tmp)) {
            wl_audio_signal* signal =
                (wl_audio_signal*)wl_malloc(sizeof(wl_audio_signal) + sizeof(wl_audio_signal_end));
            wl_audio_signal_end* signal_end = wl_null;
            WLLOGW("AUDIO_ExeCodecExtOpe READ_VOICE_DATA fail");
            if (wl_null == signal) {
                WLLOGW("malloc fail");
                return;
            }
            signal->signalid       = AUDIO_SIGNAL_RECORD_END;
            signal_end             = (wl_audio_signal_end*)signal->content;
            signal_end->resultcode = -1;
            SCI_SendSignal((void*)signal, s_audio_global.thread);
        } else {
            s_audio_global.recorder.cachelen += (framelen << 1);
            if (s_audio_global.recorder.cachelen >= RECORD_PCM_FRAME_BYTES) {
                wl_audio_signal* signal =
                    (wl_audio_signal*)wl_malloc(sizeof(wl_audio_signal) + sizeof(wl_audio_signal_record_frame));
                wl_audio_signal_record_frame* record_frame = wl_null;
                WLLOGT("AUDIO_ExeCodecExtOpe READ_VOICE_DATA success");
                if (wl_null == signal) {
                    WLLOGW("malloc fail");
                    return;
                }

                signal->signalid = AUDIO_SIGNAL_RECORD_DATA;
                record_frame     = (wl_audio_signal_record_frame*)signal->content;
                memcpy(record_frame->data, s_audio_global.recorder.cache, RECORD_PCM_FRAME_BYTES);
                record_frame->datasize = RECORD_PCM_FRAME_BYTES;
                SCI_SendSignal((void*)signal, s_audio_global.thread);

                s_audio_global.recorder.cachelen -= RECORD_PCM_FRAME_BYTES;
                if (s_audio_global.recorder.cachelen > 0) {
                    wl_char pcmframe[RECORD_PCM_FRAME_BYTES];
                    WLLOGT("recorder left data len  %d", s_audio_global.recorder.cachelen);
                    memcpy(pcmframe,
                           s_audio_global.recorder.cache + RECORD_PCM_FRAME_BYTES,
                           s_audio_global.recorder.cachelen - RECORD_PCM_FRAME_BYTES);
                    memcpy(s_audio_global.recorder.cache,
                           pcmframe,
                           s_audio_global.recorder.cachelen - RECORD_PCM_FRAME_BYTES);
                }
            }
        }
        WLLOGT("recorder data framelen %d", s_audio_global.recorder.cachelen);
    }
}

WL_AUDIO_HANDLE wl_audio_start_record(wl_audio_recorder_callback callback, wl_audio_end_callback end_callback) {
    DSP_CODEC_EXT_OPE_START_RECORD_T param;
    wl_uint32                        tmp = 0;
#    if AUDIO_VOIP_SUPPORT
    HAUDIODEV audioDev = hARMVBDualChnRecord;
#    else
    HAUDIODEV audioDev = hARMVBRECORD;
#    endif
    // 先释放录音器
    wl_audio_stop_record(s_audio_global.recorder.handle);

    wl_mutex_acquire(&s_audio_global.recorder.mutex);
    WLLOGI("start record start");
    // MMIAPICC_ReleaseCallByRedkey();
    s_audio_global.recorder.handle =
        AUDIO_CreateNormalHandle(hDSPCodec, wl_null, audioDev, wl_null, audio_notify_callback);
    if (INVALID_AUDIO_HANDLE == s_audio_global.recorder.handle) {
        WLLOGW("AUDIO_CreateNormalHandle hPCMDev fail");
        wl_mutex_release(&s_audio_global.recorder.mutex);
        return INVALID_AUDIO_HANDLE;
    }
    WLLOGI("AUDIO_CreateNormalHandle");
    param.recorder_format = AUD_REC_PCM;
    param.callback_fun    = audio_recorder_callback;
    param.hAudioHandle    = s_audio_global.recorder.handle;
    AUDIO_SetOutputSampleRate(s_audio_global.recorder.handle, RECORD_PCM_RATE);
    AUD_Cfg_PcmTrans_Param(RECORD_PCM_RATE);
    if (AUDIO_NO_ERROR
        != AUDIO_ExeCodecExtOpe(s_audio_global.recorder.handle,
                                ANSI2UINT16("EXT_START_RECORD"),
                                DSP_CODEC_EXT_OPE_START_RECORD_PARA_COUNT,
                                &param,
                                &tmp)) {
        AUDIO_CloseHandle(s_audio_global.recorder.handle);
        s_audio_global.recorder.handle = INVALID_AUDIO_HANDLE;
        wl_mutex_release(&s_audio_global.recorder.mutex);
        WLLOGI("AUDIO_ExeCodecExtOpe EXT_START_RECORD fail");
        return INVALID_AUDIO_HANDLE;
    };
    WLLOGI("AUDIO_ExeCodecExtOpe EXT_START_RECORD success");
    s_audio_global.recorder.cachelen     = 0;
    s_audio_global.recorder.callback     = callback;
    s_audio_global.recorder.end_callback = end_callback;
    wl_mutex_release(&s_audio_global.recorder.mutex);

    WLLOGI("start record success");
    return s_audio_global.recorder.handle;
}

wl_int32 wl_audio_stop_record(WL_AUDIO_HANDLE handle) {
    WL_AUDIO_HANDLE recorder_handle = INVALID_AUDIO_HANDLE;
    WLLOGI("stop record start %x", handle);
    wl_mutex_acquire(&s_audio_global.recorder.mutex);
    recorder_handle = s_audio_global.recorder.handle;
    if (handle == s_audio_global.recorder.handle) {
        s_audio_global.recorder.handle = INVALID_AUDIO_HANDLE;
    }
    wl_mutex_release(&s_audio_global.recorder.mutex);
    if (INVALID_AUDIO_HANDLE != handle && handle == recorder_handle) {
        DSP_CODEC_EXT_OPE_START_RECORD_T param;
        wl_uint32                        tmp = 0;
        param.recorder_format                = AUD_REC_PCM;
        param.callback_fun                   = audio_recorder_callback;
        param.hAudioHandle                   = handle;
        AUDIO_ExeCodecExtOpe(
            handle, ANSI2UINT16("EXT_STOP_RECORD"), DSP_CODEC_EXT_OPE_START_RECORD_PARA_COUNT, &param, &tmp);
        AUDIO_CloseHandle(handle);
        WLLOGI("EXT_STOP_RECORD devicehandle %p", handle);
    }
    WLLOGI("stop record success");
    return 0;
}

static AUDIO_STREAM_GET_SRC_RESULT_E
    audio_pcm_callback(HAUDIO hAudio, AUDIO_GETSRCDATA_INFO_T* const ptSrcDataInfo, void* pvOtherInfo) {
    WLLOGI("audio pcm callback iOffset %d cachelen %d", ptSrcDataInfo->iOffset, s_audio_global.player.cachelen);
    wl_mutex_acquire(&s_audio_global.player.mutex);
    if (INVALID_AUDIO_HANDLE == s_audio_global.player.handle) {
        WLLOGD("audio player stoped");
        wl_mutex_release(&s_audio_global.player.mutex);
        return AUDIO_STREAM_GET_SRC_ERROR;
    } else {
        // 每次只写一帧
        if (PLAY_PCM_FRAME_BYTES > s_audio_global.player.cachelen) {
            memset(s_audio_global.player.cache, 0, PLAY_PCM_FRAME_BYTES);
            s_audio_global.player.cachelen = PLAY_PCM_FRAME_BYTES;
            WLLOGI("audio_pcm_callback mute");
        }
        memcpy(s_audio_global.player.play_cache, s_audio_global.player.cache, s_audio_global.player.cachelen);
        ptSrcDataInfo->tStreamCurSrcDataInfo.pucDataAddr  = s_audio_global.player.play_cache;
        ptSrcDataInfo->tStreamCurSrcDataInfo.uiDataLength = s_audio_global.player.cachelen;
        WLLOGI("audio_pcm_callback cachelen %d", s_audio_global.player.cachelen);
        s_audio_global.player.cachelen = 0;
        // 通知GET数据
        if (s_audio_global.player.callback) {
            s_audio_global.player.callback(s_audio_global.player.handle);
        }

        // ptSrcDataInfo->iOffset = 0;
        // ptSrcDataInfo->tStreamCurSrcDataInfo.tOffsetTimeInfo.eOffsetType = AUDIO_SRCDATA_INFO_OFFSET_TIME;
        // ptSrcDataInfo->tStreamCurSrcDataInfo.tOffsetTimeInfo.uOffsetTime.uiCurTime = 0;
    }
    wl_mutex_release(&s_audio_global.player.mutex);

    return AUDIO_STREAM_GET_SRC_SUCCESS;
}

WL_AUDIO_HANDLE wl_audio_start_play(wl_audio_player_callback callback, wl_audio_end_callback end_callback) {
    WAV_STREAM_BUFFER_T      wav_header_info     = {0};
    WAV_STREAM_FORMATINFO_T* wav_format_info_ptr = &(wav_header_info.unWavStreamData.tWavFormatInfo);
#    if AUDIO_VOIP_SUPPORT
    HAUDIODEV audioDev = hARMVBDualChn;
#    else
    HAUDIODEV audioDev = hARMVB;
#    endif
    wav_format_info_ptr->eSubtype       = WAV_PCM;
    wav_format_info_ptr->uiBitPerSample = 16;
    wav_format_info_ptr->uiBlockAlign   = 4;
    wav_format_info_ptr->uiChannelNum   = 1;
    wav_format_info_ptr->uiDataSize     = sizeof(WAV_STREAM_BUFFER_T);  //(PLAY_PCM_RATE * 2 * 3600);
    wav_format_info_ptr->uiSampleRate   = PLAY_PCM_RATE;
    wav_header_info.bType               = SCI_TRUE;

    WLLOGI("start play start");
    wl_mutex_acquire(&s_audio_global.player.mutex);
    s_audio_global.player.cachelen     = 0;
    s_audio_global.player.callback     = callback;
    s_audio_global.player.end_callback = end_callback;
    s_audio_global.player.handle       = AUDIO_CreateStreamBufferHandle(hWAVCodec,
                                                                  PNULL,
                                                                  audioDev,
                                                                  PNULL,
                                                                  (uint8*)&wav_header_info,
                                                                  sizeof(WAV_STREAM_BUFFER_T),
                                                                  (uint32*)(s_audio_global.player.codec_cache),
                                                                  PLAYER_CODEC_CACHE_SIZE,
                                                                  audio_notify_callback,
                                                                  audio_pcm_callback);
    if (INVALID_AUDIO_HANDLE == s_audio_global.player.handle) {
        WLLOGW("AUDIO_CreateStreamBufferHandle fail");
        wl_mutex_release(&s_audio_global.player.mutex);
        return wl_null;
    }

#    ifdef CHANGE_AUDIO_MODE
    AUDIO_GetDevMode(&s_audio_global.player.devicemode);
    WLLOGI("start play AUDIO_GetDevMode %d", s_audio_global.player.devicemode);
    AUDIO_GetVolume(&s_audio_global.player.volume);
    WLLOGI("start play AUDIO_GetVolume %d", s_audio_global.player.volume);
    AUDIO_SetDevMode(AUDIO_DEVICE_MODE_HANDFREE);
    WLLOGI("start play AUDIO_SetDevMode %d", AUDIO_DEVICE_MODE_HANDFREE);
    AUDIO_SetDevMode(s_audio_global.player.devicemode);
    AUDIO_SetVolume(7);
    WLLOGI("start play AUDIO_SetVolume 7");
#    endif
    // AUDIO_SetVolume(7);
    WLLOGI("start play AUDIO_Play");
    if (AUDIO_NO_ERROR != AUDIO_Play(s_audio_global.player.handle, 0)) {
#    ifdef CHANGE_AUDIO_MODE
        AUDIO_SetDevMode(s_audio_global.player.devicemode);
        AUDIO_SetVolume(s_audio_global.player.volume);
#    endif
        WLLOGW("AUDIO_Play error");
        AUDIO_CloseHandle(s_audio_global.player.handle);
        s_audio_global.player.handle = INVALID_AUDIO_HANDLE;
        wl_mutex_release(&s_audio_global.player.mutex);
        return wl_null;
    }
    wl_mutex_release(&s_audio_global.player.mutex);
    WLLOGI("start play success");
    return s_audio_global.player.handle;
}

wl_int32 wl_audio_put_play_data(WL_AUDIO_HANDLE handle, wl_uint8* data, wl_uint32 data_size) {
    wl_mutex_acquire(&s_audio_global.player.mutex);
    if (INVALID_HANDLE != s_audio_global.player.handle) {
        if (data_size <= PLAY_PCM_CACHED_LEN) {
            memcpy(s_audio_global.player.cache, data, data_size);
            s_audio_global.player.cachelen = data_size;
            // WLLOGD("put play data size %d", data_size);
        }
    }
    wl_mutex_release(&s_audio_global.player.mutex);
    return 0;
}

wl_int32 wl_audio_stop_play(WL_AUDIO_HANDLE handle) {
    WLLOGI("stop play start");
    wl_mutex_acquire(&s_audio_global.player.mutex);
    WLLOGI("stop play wl_mutex_acquire");
    if (handle == s_audio_global.player.handle) {
#    ifdef CHANGE_AUDIO_MODE
        WLLOGI("stop play AUDIO_SetDevMode");
        AUDIO_SetDevMode(s_audio_global.player.devicemode);
        WLLOGI("stop play AUDIO_SetVolume");
        AUDIO_SetVolume(s_audio_global.player.volume);
#    endif
        s_audio_global.player.handle = INVALID_AUDIO_HANDLE;
    }
    wl_mutex_release(&s_audio_global.player.mutex);
    if (INVALID_AUDIO_HANDLE != handle) {
        WLLOGI("stop play AUDIO_Stop");
        AUDIO_Stop(handle);
        WLLOGI("stop play AUDIO_CloseHandle");
        AUDIO_CloseHandle(handle);
    }
    WLLOGI("stop play success");
    return 0;
}
#elif defined(WL_OS_QUECTEL)

#    if !defined(NEW_QUECTEL_AUDIO_API)
static void audio_recorder_timer_callback(WL_TIMER timer, void* context) {
    wl_mutex_acquire(&s_audio_global.recorder.mutex);
    if (INVALID_AUDIO_HANDLE == s_audio_global.recorder.handle) {
        WLLOGI("audio recorder stoped");
        if (s_audio_global.recorder.end_callback) {
            s_audio_global.recorder.end_callback(INVALID_AUDIO_HANDLE, 0);
        }
        wl_mutex_release(&s_audio_global.recorder.mutex);
        return;
    } else if (s_audio_global.recorder.timer != timer) {
        WLLOGI("audio recorder timer invalid");
        wl_timer_stop(timer);
        wl_mutex_release(&s_audio_global.recorder.mutex);
        return;
    } else {
        if (s_audio_global.recorder.cachelen + RECORD_PCM_FRAME_BYTES > RECORD_PCM_CACHED_LEN) {
            s_audio_global.recorder.cachelen = 0;
        }
        int framelen = ql_pcm_read(s_audio_global.recorder.handle,
                                   s_audio_global.recorder.cache + s_audio_global.recorder.cachelen,
                                   RECORD_PCM_FRAME_BYTES);
        if (framelen < 0) {
            WLLOGW("audio recorder ql_pcm_read error");
            if (s_audio_global.recorder.end_callback) {
                s_audio_global.recorder.end_callback(s_audio_global.recorder.handle, -1);
            }
            wl_mutex_release(&s_audio_global.recorder.mutex);
            return;
        } else {
            s_audio_global.recorder.cachelen += framelen;
            if (s_audio_global.recorder.cachelen >= RECORD_PCM_FRAME_BYTES) {
                if (s_audio_global.recorder.callback) {
                    s_audio_global.recorder.callback(
                        s_audio_global.recorder.handle, s_audio_global.recorder.cache, RECORD_PCM_FRAME_BYTES);
                }
                s_audio_global.recorder.cachelen -= RECORD_PCM_FRAME_BYTES;
                if (s_audio_global.recorder.cachelen > 0) {
                    wl_char pcmframe[RECORD_PCM_FRAME_BYTES];
                    memcpy(pcmframe,
                           s_audio_global.recorder.cache + RECORD_PCM_FRAME_BYTES,
                           s_audio_global.recorder.cachelen - RECORD_PCM_FRAME_BYTES);
                    memcpy(s_audio_global.recorder.cache,
                           pcmframe,
                           s_audio_global.recorder.cachelen - RECORD_PCM_FRAME_BYTES);
                }
            }
        }
        WLLOGT("recorder data framelen %d", s_audio_global.recorder.cachelen);
    }
    wl_mutex_release(&s_audio_global.recorder.mutex);
}

WL_AUDIO_HANDLE wl_audio_start_record(wl_audio_recorder_callback callback, wl_audio_end_callback end_callback) {
    QL_PCM_CONFIG_T config;

    // 先释放录音器
    wl_audio_stop_record(s_audio_global.recorder.handle);

    WLLOGI("start recorder start");
    wl_mutex_acquire(&s_audio_global.recorder.mutex);
    config.channels                = 1;
    config.samplerate              = RECORD_PCM_RATE;
    config.periodcnt               = 0;

    ql_set_audio_path_earphone();

    s_audio_global.recorder.handle = ql_pcm_open(&config, QL_PCM_NONBLOCK_FLAG | QL_PCM_READ_FLAG);
    if (wl_null == s_audio_global.recorder.handle) {
        WLLOGW("ql_pcm_open fail");
        wl_mutex_release(&s_audio_global.recorder.mutex);
        return wl_null;
    }
    // 启动播放定时器
    s_audio_global.recorder.timer =
        wl_timer_start(20, REPETITIVE, audio_recorder_timer_callback, wl_null, WL_TASK_MEDIA, "audio_recorder");
    if (WL_INVALID_TIMER == s_audio_global.recorder.timer) {
        ql_pcm_close(s_audio_global.recorder.handle);
        s_audio_global.recorder.handle = wl_null;
        WLLOGW("wl_timer_start fail");
        wl_mutex_release(&s_audio_global.recorder.mutex);
        return wl_null;
    }
    s_audio_global.recorder.cachelen     = 0;
    s_audio_global.recorder.callback     = callback;
    s_audio_global.recorder.end_callback = end_callback;
    wl_mutex_release(&s_audio_global.recorder.mutex);
    WLLOGI("ql_pcm_record_init handle %x success", s_audio_global.recorder.handle);
    // ql_set_audio_path_earphone();
    return s_audio_global.recorder.handle;
}

wl_int32 wl_audio_stop_record(WL_AUDIO_HANDLE handle) {
    wl_mutex_acquire(&s_audio_global.recorder.mutex);
    if (handle == s_audio_global.recorder.handle) {
        WLLOGI("stop record handle %x", s_audio_global.recorder.handle);
        s_audio_global.recorder.handle = INVALID_AUDIO_HANDLE;
        if (WL_INVALID_TIMER != s_audio_global.recorder.timer) {
            wl_timer_stop(s_audio_global.recorder.timer);
            s_audio_global.recorder.timer = WL_INVALID_TIMER;
        }
    }
    wl_mutex_release(&s_audio_global.recorder.mutex);
    if (INVALID_AUDIO_HANDLE != handle) {
        ql_pcm_close(handle);
        WLLOGI("ql_pcm_record_deinit handle %x", handle);
    }
    WLLOGI("stop record success");
    return 0;
}

static void audio_player_timer_callback(WL_TIMER timer, void* context) {
    WLLOGT("audio player start");
    wl_mutex_acquire(&s_audio_global.player.mutex);
    if (INVALID_AUDIO_HANDLE == s_audio_global.player.handle) {
        // 通知GET数据
        if (s_audio_global.player.end_callback) {
            s_audio_global.player.end_callback(s_audio_global.player.handle, 0);
        }
        wl_mutex_release(&s_audio_global.player.mutex);
        WLLOGI("audio player stoped");
        return;
    } else if (s_audio_global.player.timer != timer) {
        wl_timer_stop(timer);
        wl_mutex_release(&s_audio_global.player.mutex);
        WLLOGI("audio player timer invalid");
        return;
    } else {
        // 每次只写一帧,与定时器匹配
        if (PLAY_PCM_FRAME_BYTES > s_audio_global.player.cachelen) {
            memset(s_audio_global.player.cache, 0, PLAY_PCM_FRAME_BYTES);
        }
        WLLOGT("audio player ql_pcm_write");
        int pcm_write = ql_pcm_write(s_audio_global.player.handle, s_audio_global.player.cache, PLAY_PCM_FRAME_BYTES);
        WLLOGT("audio_pcm_callback handle %d pcm_write %d cachelen %d",
               s_audio_global.player.handle,
               pcm_write,
               s_audio_global.player.cachelen);
        s_audio_global.player.cachelen = 0;
        // 通知GET数据
        if (s_audio_global.player.callback) {
            s_audio_global.player.callback(s_audio_global.player.handle);
        }
    }
    wl_mutex_release(&s_audio_global.player.mutex);
}

WL_AUDIO_HANDLE wl_audio_start_play(wl_audio_player_callback callback, wl_audio_end_callback end_callback) {
    QL_PCM_CONFIG_T config;

    wl_audio_stop_play(s_audio_global.player.handle);

    wl_mutex_acquire(&s_audio_global.player.mutex);
    config.channels   = 1;
    config.samplerate = PLAY_PCM_RATE;
    config.periodcnt  = 0;

    ql_set_audio_path_earphone();

    s_audio_global.player.handle = ql_pcm_open(&config, QL_PCM_WRITE_FLAG | QL_PCM_NONBLOCK_FLAG);
    if (NULL == s_audio_global.player.handle) {
        WLLOGW("ql_pcm_open fail");
        wl_mutex_release(&s_audio_global.player.mutex);
        return INVALID_AUDIO_HANDLE;
    }
    if (WL_INVALID_TIMER != s_audio_global.player.timer) {
        WLLOGW("stop play timer");
        wl_timer_stop(s_audio_global.player.timer);
    }
    // 启动播放定时器
    s_audio_global.player.timer =
        wl_timer_start(20, REPETITIVE, audio_player_timer_callback, wl_null, WL_TASK_MEDIA, "audio_player");
    if (WL_INVALID_TIMER == s_audio_global.player.timer) {
        ql_pcm_close(s_audio_global.player.handle);
        s_audio_global.player.handle = INVALID_AUDIO_HANDLE;
        WLLOGW("wl_timer_start fail");
        wl_mutex_release(&s_audio_global.player.mutex);
        return INVALID_AUDIO_HANDLE;
    }
    WLLOGI("ql_pcm_open timer %d handle %x success", s_audio_global.player.timer, s_audio_global.player.handle);
    s_audio_global.player.cachelen     = 0;
    s_audio_global.player.callback     = callback;
    s_audio_global.player.end_callback = end_callback;
    wl_mutex_release(&s_audio_global.player.mutex);
    // ql_set_audio_path_earphone();
    return s_audio_global.player.handle;
}

wl_int32 wl_audio_put_play_data(WL_AUDIO_HANDLE handle, wl_uint8* data, wl_uint32 data_size) {
    wl_mutex_acquire(&s_audio_global.player.mutex);
    if (INVALID_AUDIO_HANDLE != s_audio_global.player.handle) {
        if (data_size == PLAY_PCM_FRAME_BYTES) {
            memcpy(s_audio_global.player.cache, data, data_size);
            s_audio_global.player.cachelen = data_size;
            WLLOGI("put play data size %d", data_size);
        }
    }
    wl_mutex_release(&s_audio_global.player.mutex);

    return 0;
}

wl_int32 wl_audio_stop_play(WL_AUDIO_HANDLE handle) {
    WLLOGI("stop play start handle %x player handle %x", handle, s_audio_global.player.handle);
    wl_mutex_acquire(&s_audio_global.player.mutex);
    if (handle == s_audio_global.player.handle) {
        WLLOGI("stop play wl_mutex_acquire handle %x", handle);
        s_audio_global.player.handle = INVALID_AUDIO_HANDLE;
        if (WL_INVALID_TIMER != s_audio_global.player.timer) {
            WLLOGI("stop play timer");
            wl_timer_stop(s_audio_global.player.timer);
            s_audio_global.player.timer = WL_INVALID_TIMER;
        }
    }
    wl_mutex_release(&s_audio_global.player.mutex);
    if (INVALID_AUDIO_HANDLE != handle) {
        WLLOGI("ql_pcm_close handle %x", handle);
        ql_pcm_close(handle);
    }
    WLLOGI("stop play success");
    return 0;
}
#    else

void wl_audio_record_callback(acm_audio_track_handle handle, acm_audio_record_event_t evt) {
    if (evt == AUDIO_RECORD_EVENT_TICK) {
        wl_mutex_acquire(&s_audio_global.recorder.mutex);
        if (INVALID_AUDIO_HANDLE != s_audio_global.recorder.handle) {
            unsigned int readSize = RECORD_PCM_FRAME_BYTES;
            if (s_audio_global.recorder.cachelen + RECORD_PCM_FRAME_BYTES > RECORD_PCM_CACHED_LEN) {
                s_audio_global.recorder.cachelen = 0;
            }

            int ret = ql_audio_record_read(
                handle, s_audio_global.recorder.cache + s_audio_global.recorder.cachelen, &readSize);

            // WLLOGD("ql_audio_record_read ret:%d size:%d", ret, readSize);
            if (ret != 0) {
                WLLOGD("audio recorder ql_pcm_read error");

                ql_audio_record_close((audio_record_handle)s_audio_global.recorder.handle, 1);
                s_audio_global.recorder.handle = INVALID_AUDIO_HANDLE;

                if (s_audio_global.recorder.end_callback) {
                    s_audio_global.recorder.end_callback(s_audio_global.recorder.handle, -1);
                }
                wl_mutex_release(&s_audio_global.recorder.mutex);
                return;
            } else {
                s_audio_global.recorder.cachelen += readSize;
                if (s_audio_global.recorder.cachelen >= RECORD_PCM_FRAME_BYTES) {
                    if (s_audio_global.recorder.callback) {
                        s_audio_global.recorder.callback(
                            s_audio_global.recorder.handle, s_audio_global.recorder.cache, RECORD_PCM_FRAME_BYTES);
                    }
                    s_audio_global.recorder.cachelen -= RECORD_PCM_FRAME_BYTES;
                    if (s_audio_global.recorder.cachelen > 0) {
                        wl_char pcmframe[RECORD_PCM_FRAME_BYTES];
                        memcpy(pcmframe,
                               s_audio_global.recorder.cache + RECORD_PCM_FRAME_BYTES,
                               s_audio_global.recorder.cachelen - RECORD_PCM_FRAME_BYTES);
                        memcpy(s_audio_global.recorder.cache,
                               pcmframe,
                               s_audio_global.recorder.cachelen - RECORD_PCM_FRAME_BYTES);
                    }
                }
            }
            // WLLOGD("recorder data framelen %d", s_audio_global.recorder.cachelen);
        }
        wl_mutex_release(&s_audio_global.recorder.mutex);
    } else if (evt == AUDIO_RECORD_EVENT_CLOSED) {
        WLLOGD("recorder event closed %p", s_audio_global.recorder.handle);
        // wl_mutex_acquire(&s_audio_global.recorder.mutex);
        wl_audio_end_callback end_callback = s_audio_global.recorder.end_callback;
        if (end_callback) {
            end_callback(INVALID_AUDIO_HANDLE, 0);
        }
        // wl_mutex_release(&s_audio_global.recorder.mutex);
    }
}

wl_int32 wl_audio_stop_record(WL_AUDIO_HANDLE handle) {
    WLLOGD("stop record handle globale handle %x   handle:%x", s_audio_global.recorder.handle, handle);
    if (handle == INVALID_AUDIO_HANDLE) {
        return 0;
    }

    wl_mutex_acquire(&s_audio_global.recorder.mutex);
    if (handle == s_audio_global.recorder.handle) {
        s_audio_global.recorder.handle = INVALID_AUDIO_HANDLE;
    }
    wl_mutex_release(&s_audio_global.recorder.mutex);

    if (INVALID_AUDIO_HANDLE != handle) {
        ql_audio_record_close((audio_record_handle)handle, 1);
        WLLOGD("ql_pcm_record_deinit handle %x", handle);
    }
    return 0;
}

WL_AUDIO_HANDLE wl_audio_start_record(wl_audio_recorder_callback callback, wl_audio_end_callback end_callback) {
    // 先释放录音器
    wl_audio_stop_record(s_audio_global.recorder.handle);

    WLLOGD("start recorder start");
    wl_mutex_acquire(&s_audio_global.recorder.mutex);
    ql_codec_choose(AUD_INTERNAL_CODEC, NULL);
    ql_set_audio_path_receiver();
    AUDIO_RECORD_OPTION option;
    option.option = 0;
    option.gain   = 3;

    s_audio_global.recorder.handle = ql_audio_record_open(RECORD_PCM_RATE, 1, option.option, wl_audio_record_callback);
    if (0 == s_audio_global.recorder.handle) {
        WLLOGD("ql_audio_record_open fail");
        wl_mutex_release(&s_audio_global.recorder.mutex);
        return wl_null;
    }

    s_audio_global.recorder.cachelen     = 0;
    s_audio_global.recorder.callback     = callback;
    s_audio_global.recorder.end_callback = end_callback;
    wl_mutex_release(&s_audio_global.recorder.mutex);
    WLLOGD("ql_audio_record_open handle %x success", s_audio_global.recorder.handle);
    return (WL_AUDIO_HANDLE)s_audio_global.recorder.handle;
}

void wl_audio_play_callback(acm_audio_track_handle handle, acm_audio_track_event_t evt) {
    if (evt == AUDIO_TRACK_EVENT_TICK) {
        wl_mutex_acquire(&s_audio_global.player.mutex);
        if (INVALID_AUDIO_HANDLE != s_audio_global.player.handle) {
            // 每次只写一帧,与定时器匹配
            if (PLAY_PCM_FRAME_BYTES > s_audio_global.player.cachelen) {
                memset(s_audio_global.player.cache, 0, PLAY_PCM_FRAME_BYTES);
            }
            // WLLOGD("audio player ql_audio_track_write");
            int ret =
                ql_audio_track_write(s_audio_global.player.handle, s_audio_global.player.cache, PLAY_PCM_FRAME_BYTES);
            // WLLOGD("ql_audio_track_write handle %d ret %d cachelen %d",
            //        s_audio_global.player.handle,
            //        ret,
            //        s_audio_global.player.cachelen);
            s_audio_global.player.cachelen = 0;
            // 通知GET数据
            if (s_audio_global.player.callback) {
                s_audio_global.player.callback(s_audio_global.player.handle);
            }
        }
        wl_mutex_release(&s_audio_global.player.mutex);
    } else if (evt == AUDIO_TRACK_EVENT_CLOSED) {
        WLLOGW("wl_audio_play_callback AUDIO_TRACK_EVENT_CLOSED");
        //wl_mutex_acquire(&s_audio_global.player.mutex);
        wl_audio_end_callback end_callback = s_audio_global.player.end_callback;
        if (end_callback) {
            end_callback(INVALID_AUDIO_HANDLE, 0);
        }
        //wl_mutex_release(&s_audio_global.player.mutex);
        WLLOGW("after AUDIO_TRACK_EVENT_CLOSED");
    }
}

WL_AUDIO_HANDLE wl_audio_start_play(wl_audio_player_callback callback, wl_audio_end_callback end_callback) {
    wlsdk_enablePA pa_callback = wlsdk_get_PA_control_callback();
    wl_audio_stop_play(s_audio_global.player.handle);
    wl_mutex_acquire(&s_audio_global.player.mutex);

    if (pa_callback) {
        pa_callback(1);
    }

    ql_set_audio_path_speaker();

    AUDIO_PLAY_OPTION option;
    option.option       = 0;
    option.write_mode   = AUDIO_TRACK_WRITE_MODE_ASYNC;
    option.mode         = 1;
    option.eq           = 64;
    option.active_point = 0;

    s_audio_global.player.handle = ql_audio_track_open(PLAY_PCM_RATE, 1, option.option, wl_audio_play_callback);
    if (s_audio_global.player.handle == 0) {
        wl_mutex_release(&s_audio_global.player.mutex);
        if (pa_callback) {
            pa_callback(0);
        }
        return INVALID_AUDIO_HANDLE;
    }

    s_audio_global.player.cachelen     = 0;
    s_audio_global.player.callback     = callback;
    s_audio_global.player.end_callback = end_callback;
    wl_mutex_release(&s_audio_global.player.mutex);
    return (WL_AUDIO_HANDLE)s_audio_global.player.handle;
}

wl_int32 wl_audio_stop_play(WL_AUDIO_HANDLE handle) {
    int ret = -1;
    wlsdk_enablePA pa_callback = wlsdk_get_PA_control_callback();
    WLLOGW("stop play global handle %x  handle %x", s_audio_global.player.handle, handle);
    if (handle == INVALID_AUDIO_HANDLE) {
        return 0;
    }

    wl_mutex_acquire(&s_audio_global.player.mutex);
    if (handle == s_audio_global.player.handle) {
        s_audio_global.player.handle = INVALID_AUDIO_HANDLE;
    }
    wl_mutex_release(&s_audio_global.player.mutex);
    ret = ql_audio_track_close(handle, 1);
    WLLOGW("ql_audio_track_close handle %x ret:%d", handle, ret);
    if (pa_callback) {
        pa_callback(0);
    }
    return 0;
}

wl_int32 wl_audio_put_play_data(WL_AUDIO_HANDLE handle, wl_uint8* data, wl_uint32 data_size) {
    wl_mutex_acquire(&s_audio_global.player.mutex);
    if (INVALID_AUDIO_HANDLE != s_audio_global.player.handle) {
        if (data_size == PLAY_PCM_FRAME_BYTES) {
            memcpy(s_audio_global.player.cache, data, data_size);
            s_audio_global.player.cachelen = data_size;
            WLLOGI("put play data size %d", data_size);
        }
    }
    wl_mutex_release(&s_audio_global.player.mutex);

    return 0;
}

#    endif
#else
void SDLCALL sdl_record_audio_callback(void* userdata, Uint8* stream, int len) {
    wl_mutex_acquire(&s_audio_global.recorder.mutex);
    wl_audio_recorder* recorder = (wl_audio_recorder*)userdata;
    if (recorder->callback) {
        recorder->callback(recorder->handle, stream, len);
    }
    wl_mutex_release(&s_audio_global.recorder.mutex);
}

void SDLCALL sdl_play_audio_callback(void* userdata, Uint8* stream, int len) {
    audio_get_play_data(stream, len);
}

WL_AUDIO_HANDLE wl_audio_start_record(wl_audio_recorder_callback callback, wl_audio_end_callback end_callback) {
    wl_mutex_acquire(&s_audio_global.recorder.mutex);
    SDL_AudioSpec      inspec, outspec;
    wl_audio_recorder* recorder = &s_audio_global.recorder;
    memset(recorder, 0, sizeof(wl_audio_recorder));
    recorder->callback     = callback;
    recorder->end_callback = end_callback;
    memset(&inspec, 0, sizeof(SDL_AudioSpec));
    memset(&outspec, 0, sizeof(SDL_AudioSpec));
    inspec.format   = AUDIO_U16LSB;
    inspec.channels = 1;
    inspec.freq     = RECORD_PCM_RATE;
    inspec.samples  = RECORD_PCM_RATE / 50;
    inspec.callback = sdl_record_audio_callback;
    inspec.userdata = recorder;
    int count       = SDL_GetNumAudioDevices(SDL_TRUE);
    for (int i = 0; i < count; i++) {
        WLLOGI("name:%s", SDL_GetAudioDeviceName(i, SDL_TRUE));
        // SDL_Delay(100);
        SDL_CloseAudioDevice(i);
    }
    recorder->handle = SDL_OpenAudioDevice(
        SDL_GetAudioDeviceName(0, SDL_TRUE), SDL_TRUE, &inspec, &outspec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
    if (recorder->handle > 0) {
        SDL_PauseAudioDevice(recorder->handle, 0);
        wl_mutex_release(&s_audio_global.recorder.mutex);
        return recorder->handle;
    }
    wl_mutex_release(&s_audio_global.recorder.mutex);
    return INVALID_AUDIO_HANDLE;
}

wl_int32 wl_audio_stop_record(WL_AUDIO_HANDLE handle) {
    if (handle) {
        // SDL_PauseAudioDevice(audioDevice, 0);
        // SDL_Delay(500);
        SDL_CloseAudioDevice(handle);
    }
    wl_mutex_acquire(&s_audio_global.recorder.mutex);
    if (handle == s_audio_global.recorder.handle) {
        s_audio_global.recorder.handle = INVALID_AUDIO_HANDLE;
    }
    wl_mutex_release(&s_audio_global.recorder.mutex);
    return 0;
}

WL_AUDIO_HANDLE wl_audio_start_play(wl_audio_player_callback callback, wl_audio_end_callback end_callback) {
    wl_mutex_acquire(&s_audio_global.player.mutex);
    SDL_AudioSpec    inspec, outspec;
    wl_audio_player* player = &s_audio_global.player;
    memset(player, 0, sizeof(wl_audio_player));
    player->callback     = callback;
    player->end_callback = end_callback;
    memset(&inspec, 0, sizeof(SDL_AudioSpec));
    memset(&outspec, 0, sizeof(SDL_AudioSpec));
    inspec.format   = AUDIO_U16LSB;
    inspec.channels = 1;
    inspec.freq     = PLAY_PCM_RATE;
    inspec.samples  = PLAY_PCM_RATE / 50;
    inspec.callback = sdl_play_audio_callback;
    inspec.userdata = player;
    int count       = SDL_GetNumAudioDevices(SDL_FALSE);
    for (int i = 0; i < count; i++) {
        WLLOGI("name:%s", SDL_GetAudioDeviceName(i, SDL_FALSE));
        // SDL_Delay(100);
        SDL_CloseAudioDevice(i);
    }
    player->handle = SDL_OpenAudioDevice(
        SDL_GetAudioDeviceName(0, SDL_FALSE), SDL_FALSE, &inspec, &outspec, SDL_AUDIO_ALLOW_ANY_CHANGE);
    if (player->handle > 0) {
        SDL_PauseAudioDevice(player->handle, 1);
        SDL_PauseAudioDevice(player->handle, 0);
        wl_mutex_release(&s_audio_global.player.mutex);
        return player->handle;
    } else {
        wl_mutex_release(&s_audio_global.player.mutex);
        return wl_null;
    }
}

wl_int32 wl_audio_put_play_data(WL_AUDIO_HANDLE handle, wl_uint8* data, wl_uint32 data_size) {
    return audio_put_play_data(handle, data, data_size);
}

wl_int32 wl_audio_stop_play(WL_AUDIO_HANDLE handle) {
    if (handle) {
        // SDL_PauseAudioDevice(handle, 1);
        // SDL_Delay(100);
        SDL_CloseAudioDevice(handle);
    }
    wl_mutex_acquire(&s_audio_global.player.mutex);
    if (handle == s_audio_global.player.handle) {
        s_audio_global.player.handle = INVALID_AUDIO_HANDLE;
    }
    wl_mutex_release(&s_audio_global.player.mutex);
    return 0;
}
#endif

wl_int32 audio_get_play_data(wl_uint8* data, wl_uint32 datalen) {
    wl_mutex_acquire(&s_audio_global.player.mutex);
    if (s_audio_global.player.cachelen >= datalen) {
        memcpy(data, s_audio_global.player.cache, datalen);
        s_audio_global.player.cachelen -= datalen;
        if (s_audio_global.player.cachelen > 0) {
            memmove(s_audio_global.player.cache, s_audio_global.player.cache + datalen, s_audio_global.player.cachelen);
        }
        // 缓存区未满,拉取数据
        if (s_audio_global.player.cachelen <= (PLAY_PCM_CACHED_LEN - PLAY_PCM_FRAME_BYTES)) {
            // 通知GET数据
            if (s_audio_global.player.callback) {
                s_audio_global.player.callback(s_audio_global.player.handle);
            }
        }
        wl_mutex_release(&s_audio_global.player.mutex);
        return 0;
    }
    memset(data, 0, datalen);
    // 通知GET数据
    if (s_audio_global.player.callback) {
        s_audio_global.player.callback(s_audio_global.player.handle);
    }
    wl_mutex_release(&s_audio_global.player.mutex);
    return -1;
}

wl_int32 audio_put_play_data(WL_AUDIO_HANDLE handle, wl_uint8* data, wl_uint32 data_size) {
    wl_mutex_acquire(&s_audio_global.player.mutex);
    if (INVALID_AUDIO_HANDLE != s_audio_global.player.handle) {
        if (data_size <= PLAY_PCM_CACHED_LEN - s_audio_global.player.cachelen) {
            memcpy(s_audio_global.player.cache + s_audio_global.player.cachelen, data, data_size);
            s_audio_global.player.cachelen += data_size;
        }
    }
    wl_mutex_release(&s_audio_global.player.mutex);
    return 0;
}

#ifdef __cplusplus
}
#endif /* __cplusplus */
